home *** CD-ROM | disk | FTP | other *** search
/ Aminet 52 / Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso / Aminet / dev / gg / ncurses-5.3.lha / ncurses-5.3 / test / bs.c < prev    next >
C/C++ Source or Header  |  2002-10-24  |  28KB  |  1,229 lines

  1. /* 
  2.  * bs.c - original author: Bruce Holloway
  3.  *        salvo option by: Chuck A DeGaul
  4.  * with improved user interface, autoconfiguration and code cleanup
  5.  *        by Eric S. Raymond <esr@snark.thyrsus.com>
  6.  * v1.2 with color support and minor portability fixes, November 1990
  7.  * v2.0 featuring strict ANSI/POSIX conformance, November 1993.
  8.  * v2.1 with ncurses mouse support, September 1995
  9.  *
  10.  * $Id: bs.c,v 1.36 2002/06/29 23:32:18 tom Exp $
  11.  */
  12.  
  13. #include <ctype.h>
  14. #include <assert.h>
  15. #include <time.h>
  16.  
  17. #include <test.priv.h>
  18.  
  19. #ifndef SIGIOT
  20. #define SIGIOT SIGABRT
  21. #endif
  22.  
  23. static int getcoord(int);
  24.  
  25. /*
  26.  * Constants for tuning the random-fire algorithm. It prefers moves that
  27.  * diagonal-stripe the board with a stripe separation of srchstep. If
  28.  * no such preferred moves are found, srchstep is decremented.
  29.  */
  30. #define BEGINSTEP    3    /* initial value of srchstep */
  31.  
  32. /* miscellaneous constants */
  33. #define SHIPTYPES    5
  34. #define    OTHER        (1-turn)
  35. #define PLAYER        0
  36. #define COMPUTER    1
  37. #define MARK_HIT    'H'
  38. #define MARK_MISS    'o'
  39. #define CTRLC        '\003'    /* used as terminate command */
  40. #define FF        '\014'    /* used as redraw command */
  41.  
  42. /* coordinate handling */
  43. #define BWIDTH        10
  44. #define BDEPTH        10
  45.  
  46. /* display symbols */
  47. #define SHOWHIT        '*'
  48. #define SHOWSPLASH    ' '
  49. #define IS_SHIP(c)    (isupper(UChar(c)) ? TRUE : FALSE)
  50.  
  51. /* how to position us on player board */
  52. #define PYBASE    3
  53. #define PXBASE    3
  54. #define PY(y)    (PYBASE + (y))
  55. #define PX(x)    (PXBASE + (x)*3)
  56. #define pgoto(y, x)    (void)move(PY(y), PX(x))
  57.  
  58. /* how to position us on cpu board */
  59. #define CYBASE    3
  60. #define CXBASE    48
  61. #define CY(y)    (CYBASE + (y))
  62. #define CX(x)    (CXBASE + (x)*3)
  63. #define CYINV(y)    ((y) - CYBASE)
  64. #define CXINV(x)    (((x) - CXBASE) / 3)
  65. #define cgoto(y, x)    (void)move(CY(y), CX(x))
  66.  
  67. #define ONBOARD(x, y)    (x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH)
  68.  
  69. /* other board locations */
  70. #define COLWIDTH    80
  71. #define PROMPTLINE    21    /* prompt line */
  72. #define SYBASE        CYBASE + BDEPTH + 3    /* move key diagram */
  73. #define SXBASE        63
  74. #define MYBASE        SYBASE - 1    /* diagram caption */
  75. #define MXBASE        64
  76. #define HYBASE        SYBASE - 1    /* help area */
  77. #define HXBASE        0
  78.  
  79. /* this will need to be changed if BWIDTH changes */
  80. static char numbers[] = "   0  1  2  3  4  5  6  7  8  9";
  81.  
  82. static char carrier[] = "Aircraft Carrier";
  83. static char battle[] = "Battleship";
  84. static char sub[] = "Submarine";
  85. static char destroy[] = "Destroyer";
  86. static char ptboat[] = "PT Boat";
  87.  
  88. static char name[40];
  89. static char dftname[] = "stranger";
  90.  
  91. /* direction constants */
  92. #define E    0
  93. #define SE    1
  94. #define S    2
  95. #define SW    3
  96. #define W    4
  97. #define NW    5
  98. #define N    6
  99. #define NE    7
  100. static int xincr[8] =
  101. {1, 1, 0, -1, -1, -1, 0, 1};
  102. static int yincr[8] =
  103. {0, 1, 1, 1, 0, -1, -1, -1};
  104.  
  105. /* current ship position and direction */
  106. static int curx = (BWIDTH / 2);
  107. static int cury = (BDEPTH / 2);
  108.  
  109. typedef struct {
  110.     char *name;            /* name of the ship type */
  111.     int hits;            /* how many times has this ship been hit? */
  112.     char symbol;        /* symbol for game purposes */
  113.     int length;            /* length of ship */
  114.     int x, y;            /* coordinates of ship start point */
  115.     int dir;            /* direction of `bow' */
  116.     bool placed;        /* has it been placed on the board? */
  117. } ship_t;
  118.  
  119. static bool checkplace(int b, ship_t * ss, int vis);
  120.  
  121. #define SHIPIT(name, symbol, length) { name, 0, symbol, length, 0,0, 0, FALSE }
  122.  
  123. static ship_t plyship[SHIPTYPES] =
  124. {
  125.     SHIPIT(carrier, 'A', 5),
  126.     SHIPIT(battle, 'B', 4),
  127.     SHIPIT(destroy, 'D', 3),
  128.     SHIPIT(sub, 'S', 3),
  129.     SHIPIT(ptboat, 'P', 2),
  130. };
  131.  
  132. static ship_t cpuship[SHIPTYPES] =
  133. {
  134.     SHIPIT(carrier, 'A', 5),
  135.     SHIPIT(battle, 'B', 4),
  136.     SHIPIT(destroy, 'D', 3),
  137.     SHIPIT(sub, 'S', 3),
  138.     SHIPIT(ptboat, 'P', 2),
  139. };
  140.  
  141. /* "Hits" board, and main board. */
  142. static char hits[2][BWIDTH][BDEPTH];
  143. static char board[2][BWIDTH][BDEPTH];
  144.  
  145. static int turn;        /* 0=player, 1=computer */
  146. static int plywon = 0, cpuwon = 0;    /* How many games has each won? */
  147.  
  148. static int salvo, blitz, closepack;
  149.  
  150. #define    PR    (void)addstr
  151.  
  152. static RETSIGTYPE uninitgame(int sig) GCC_NORETURN;
  153.  
  154. static RETSIGTYPE uninitgame(int sig GCC_UNUSED)
  155. /* end the game, either normally or due to signal */
  156. {
  157.     clear();
  158.     (void) refresh();
  159.     (void) reset_shell_mode();
  160.     (void) echo();
  161.     (void) endwin();
  162.     ExitProgram(sig ? EXIT_FAILURE : EXIT_SUCCESS);
  163. }
  164.  
  165. static void
  166. announceopts(void)
  167. /* announce which game options are enabled */
  168. {
  169.     if (salvo || blitz || closepack) {
  170.     (void) printw("Playing optional game (");
  171.     if (salvo)
  172.         (void) printw("salvo, ");
  173.     else
  174.         (void) printw("nosalvo, ");
  175.     if (blitz)
  176.         (void) printw("blitz ");
  177.     else
  178.         (void) printw("noblitz, ");
  179.     if (closepack)
  180.         (void) printw("closepack)");
  181.     else
  182.         (void) printw("noclosepack)");
  183.     } else
  184.     (void) printw(
  185.              "Playing standard game (noblitz, nosalvo, noclosepack)");
  186. }
  187.  
  188. static void
  189. intro(void)
  190. {
  191.     char *tmpname;
  192.  
  193.     srand((unsigned) (time(0L) + getpid()));    /* Kick the random number generator */
  194.  
  195.     (void) signal(SIGINT, uninitgame);
  196.     (void) signal(SIGINT, uninitgame);
  197.     (void) signal(SIGIOT, uninitgame);    /* for assert(3) */
  198.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  199.     (void) signal(SIGQUIT, uninitgame);
  200.  
  201.     if ((tmpname = getlogin()) != 0) {
  202.     (void) strcpy(name, tmpname);
  203.     name[0] = toupper(name[0]);
  204.     } else
  205.     (void) strcpy(name, dftname);
  206.  
  207.     (void) initscr();
  208.     keypad(stdscr, TRUE);
  209.     (void) def_prog_mode();
  210.     (void) nonl();
  211.     (void) cbreak();
  212.     (void) noecho();
  213.  
  214. #ifdef PENGUIN
  215.     (void) clear();
  216.     (void) mvaddstr(4, 29, "Welcome to Battleship!");
  217.     (void) move(8, 0);
  218.     PR("                                                  \\\n");
  219.     PR("                           \\                     \\ \\\n");
  220.     PR("                          \\ \\                   \\ \\ \\_____________\n");
  221.     PR("                         \\ \\ \\_____________      \\ \\/            |\n");
  222.     PR("                          \\ \\/             \\      \\/             |\n");
  223.     PR("                           \\/               \\_____/              |__\n");
  224.     PR("           ________________/                                       |\n");
  225.     PR("           \\  S.S. Penguin                                         |\n");
  226.     PR("            \\                                                     /\n");
  227.     PR("             \\___________________________________________________/\n");
  228.  
  229.     (void) mvaddstr(22, 27, "Hit any key to continue...");
  230.     (void) refresh();
  231.     (void) getch();
  232. #endif /* PENGUIN */
  233.  
  234. #ifdef A_COLOR
  235.     start_color();
  236.  
  237.     init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
  238.     init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
  239.     init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
  240.     init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
  241.     init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
  242.     init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
  243.     init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
  244.     init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
  245. #endif /* A_COLOR */
  246.  
  247. #ifdef NCURSES_MOUSE_VERSION
  248.     (void) mousemask(BUTTON1_CLICKED, (mmask_t *) NULL);
  249. #endif /* NCURSES_MOUSE_VERSION */
  250. }
  251.  
  252. /* VARARGS1 */
  253. static void
  254. prompt(int n, NCURSES_CONST char *f, const char *s)
  255. /* print a message at the prompt line */
  256. {
  257.     (void) move(PROMPTLINE + n, 0);
  258.     (void) clrtoeol();
  259.     (void) printw(f, s);
  260.     (void) refresh();
  261. }
  262.  
  263. static void
  264. error(NCURSES_CONST char *s)
  265. {
  266.     (void) move(PROMPTLINE + 2, 0);
  267.     (void) clrtoeol();
  268.     if (s) {
  269.     (void) addstr(s);
  270.     (void) beep();
  271.     }
  272. }
  273.  
  274. static void
  275. placeship(int b, ship_t * ss, int vis)
  276. {
  277.     int l;
  278.  
  279.     for (l = 0; l < ss->length; ++l) {
  280.     int newx = ss->x + l * xincr[ss->dir];
  281.     int newy = ss->y + l * yincr[ss->dir];
  282.  
  283.     board[b][newx][newy] = ss->symbol;
  284.     if (vis) {
  285.         pgoto(newy, newx);
  286.         (void) addch((chtype) ss->symbol);
  287.     }
  288.     }
  289.     ss->hits = 0;
  290. }
  291.  
  292. static int
  293. rnd(int n)
  294. {
  295.     return (((rand() & 0x7FFF) % n));
  296. }
  297.  
  298. static void
  299. randomplace(int b, ship_t * ss)
  300. /* generate a valid random ship placement into px,py */
  301. {
  302.  
  303.     do {
  304.     ss->dir = rnd(2) ? E : S;
  305.     ss->x = rnd(BWIDTH - (ss->dir == E ? ss->length : 0));
  306.     ss->y = rnd(BDEPTH - (ss->dir == S ? ss->length : 0));
  307.     } while
  308.     (!checkplace(b, ss, FALSE));
  309. }
  310.  
  311. static void
  312. initgame(void)
  313. {
  314.     int i, j, unplaced;
  315.     ship_t *ss;
  316.  
  317.     (void) clear();
  318.     (void) mvaddstr(0, 35, "BATTLESHIPS");
  319.     (void) move(PROMPTLINE + 2, 0);
  320.     announceopts();
  321.  
  322.     memset(board, 0, sizeof(char) * BWIDTH * BDEPTH * 2);
  323.     memset(hits, 0, sizeof(char) * BWIDTH * BDEPTH * 2);
  324.     for (i = 0; i < SHIPTYPES; i++) {
  325.     ss = cpuship + i;
  326.  
  327.     ss->x =
  328.         ss->y =
  329.         ss->dir =
  330.         ss->hits = 0;
  331.     ss->placed = FALSE;
  332.  
  333.     ss = plyship + i;
  334.  
  335.     ss->x =
  336.         ss->y =
  337.         ss->dir =
  338.         ss->hits = 0;
  339.     ss->placed = FALSE;
  340.     }
  341.  
  342.     /* draw empty boards */
  343.     (void) mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board");
  344.     (void) mvaddstr(PYBASE - 1, PXBASE - 3, numbers);
  345.     for (i = 0; i < BDEPTH; ++i) {
  346.     (void) mvaddch(PYBASE + i, PXBASE - 3, (chtype) (i + 'A'));
  347. #ifdef A_COLOR
  348.     if (has_colors())
  349.         attron(COLOR_PAIR(COLOR_BLUE));
  350. #endif /* A_COLOR */
  351.     (void) addch(' ');
  352.     for (j = 0; j < BWIDTH; j++)
  353.         (void) addstr(" . ");
  354. #ifdef A_COLOR
  355.     attrset(0);
  356. #endif /* A_COLOR */
  357.     (void) addch(' ');
  358.     (void) addch((chtype) (i + 'A'));
  359.     }
  360.     (void) mvaddstr(PYBASE + BDEPTH, PXBASE - 3, numbers);
  361.     (void) mvaddstr(CYBASE - 2, CXBASE + 7, "Hit/Miss Board");
  362.     (void) mvaddstr(CYBASE - 1, CXBASE - 3, numbers);
  363.     for (i = 0; i < BDEPTH; ++i) {
  364.     (void) mvaddch(CYBASE + i, CXBASE - 3, (chtype) (i + 'A'));
  365. #ifdef A_COLOR
  366.     if (has_colors())
  367.         attron(COLOR_PAIR(COLOR_BLUE));
  368. #endif /* A_COLOR */
  369.     (void) addch(' ');
  370.     for (j = 0; j < BWIDTH; j++)
  371.         (void) addstr(" . ");
  372. #ifdef A_COLOR
  373.     attrset(0);
  374. #endif /* A_COLOR */
  375.     (void) addch(' ');
  376.     (void) addch((chtype) (i + 'A'));
  377.     }
  378.  
  379.     (void) mvaddstr(CYBASE + BDEPTH, CXBASE - 3, numbers);
  380.  
  381.     (void) mvprintw(HYBASE, HXBASE,
  382.             "To position your ships: move the cursor to a spot, then");
  383.     (void) mvprintw(HYBASE + 1, HXBASE,
  384.             "type the first letter of a ship type to select it, then");
  385.     (void) mvprintw(HYBASE + 2, HXBASE,
  386.             "type a direction ([hjkl] or [4862]), indicating how the");
  387.     (void) mvprintw(HYBASE + 3, HXBASE,
  388.             "ship should be pointed. You may also type a ship letter");
  389.     (void) mvprintw(HYBASE + 4, HXBASE,
  390.             "followed by `r' to position it randomly, or type `R' to");
  391.     (void) mvprintw(HYBASE + 5, HXBASE,
  392.             "place all remaining ships randomly.");
  393.  
  394.     (void) mvaddstr(MYBASE, MXBASE, "Aiming keys:");
  395.     (void) mvaddstr(SYBASE, SXBASE, "y k u    7 8 9");
  396.     (void) mvaddstr(SYBASE + 1, SXBASE, " \\|/      \\|/ ");
  397.     (void) mvaddstr(SYBASE + 2, SXBASE, "h-+-l    4-+-6");
  398.     (void) mvaddstr(SYBASE + 3, SXBASE, " /|\\      /|\\ ");
  399.     (void) mvaddstr(SYBASE + 4, SXBASE, "b j n    1 2 3");
  400.  
  401.     /* have the computer place ships */
  402.     for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++) {
  403.     randomplace(COMPUTER, ss);
  404.     placeship(COMPUTER, ss, FALSE);
  405.     }
  406.  
  407.     ss = (ship_t *) NULL;
  408.     do {
  409.     char c, docked[SHIPTYPES + 2], *cp = docked;
  410.  
  411.     /* figure which ships still wait to be placed */
  412.     *cp++ = 'R';
  413.     for (i = 0; i < SHIPTYPES; i++)
  414.         if (!plyship[i].placed)
  415.         *cp++ = plyship[i].symbol;
  416.     *cp = '\0';
  417.  
  418.     /* get a command letter */
  419.     prompt(1, "Type one of [%s] to pick a ship.", docked + 1);
  420.     do {
  421.         c = getcoord(PLAYER);
  422.     } while
  423.         (!strchr(docked, c));
  424.  
  425.     if (c == 'R')
  426.         (void) ungetch('R');
  427.     else {
  428.         /* map that into the corresponding symbol */
  429.         for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
  430.         if (ss->symbol == c)
  431.             break;
  432.  
  433.         prompt(1, "Type one of [hjklrR] to place your %s.", ss->name);
  434.         pgoto(cury, curx);
  435.     }
  436.  
  437.     do {
  438.         c = getch();
  439.     } while
  440.         (!strchr("hjklrR", c) || c == FF);
  441.  
  442.     if (c == FF) {
  443.         (void) clearok(stdscr, TRUE);
  444.         (void) refresh();
  445.     } else if (c == 'r') {
  446.         prompt(1, "Random-placing your %s", ss->name);
  447.         randomplace(PLAYER, ss);
  448.         placeship(PLAYER, ss, TRUE);
  449.         error((char *) NULL);
  450.         ss->placed = TRUE;
  451.     } else if (c == 'R') {
  452.         prompt(1, "Placing the rest of your fleet at random...", "");
  453.         for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
  454.         if (!ss->placed) {
  455.             randomplace(PLAYER, ss);
  456.             placeship(PLAYER, ss, TRUE);
  457.             ss->placed = TRUE;
  458.         }
  459.         error((char *) NULL);
  460.     } else if (strchr("hjkl8462", c)) {
  461.         ss->x = curx;
  462.         ss->y = cury;
  463.  
  464.         switch (c) {
  465.         case 'k':
  466.         case '8':
  467.         ss->dir = N;
  468.         break;
  469.         case 'j':
  470.         case '2':
  471.         ss->dir = S;
  472.         break;
  473.         case 'h':
  474.         case '4':
  475.         ss->dir = W;
  476.         break;
  477.         case 'l':
  478.         case '6':
  479.         ss->dir = E;
  480.         break;
  481.         }
  482.  
  483.         if (checkplace(PLAYER, ss, TRUE)) {
  484.         placeship(PLAYER, ss, TRUE);
  485.         error((char *) NULL);
  486.         ss->placed = TRUE;
  487.         }
  488.     }
  489.  
  490.     for (unplaced = i = 0; i < SHIPTYPES; i++)
  491.         unplaced += !plyship[i].placed;
  492.     } while
  493.     (unplaced);
  494.  
  495.     turn = rnd(2);
  496.  
  497.     (void) mvprintw(HYBASE, HXBASE,
  498.             "To fire, move the cursor to your chosen aiming point   ");
  499.     (void) mvprintw(HYBASE + 1, HXBASE,
  500.             "and strike any key other than a motion key.            ");
  501.     (void) mvprintw(HYBASE + 2, HXBASE,
  502.             "                                                       ");
  503.     (void) mvprintw(HYBASE + 3, HXBASE,
  504.             "                                                       ");
  505.     (void) mvprintw(HYBASE + 4, HXBASE,
  506.             "                                                       ");
  507.     (void) mvprintw(HYBASE + 5, HXBASE,
  508.             "                                                       ");
  509.  
  510.     (void) prompt(0, "Press any key to start...", "");
  511.     (void) getch();
  512. }
  513.  
  514. static int
  515. getcoord(int atcpu)
  516. {
  517.     int ny, nx, c;
  518.  
  519.     if (atcpu)
  520.     cgoto(cury, curx);
  521.     else
  522.     pgoto(cury, curx);
  523.     (void) refresh();
  524.     for (;;) {
  525.     if (atcpu) {
  526.         (void) mvprintw(CYBASE + BDEPTH + 1, CXBASE + 11, "(%d, %c)",
  527.                 curx, 'A' + cury);
  528.         cgoto(cury, curx);
  529.     } else {
  530.         (void) mvprintw(PYBASE + BDEPTH + 1, PXBASE + 11, "(%d, %c)",
  531.                 curx, 'A' + cury);
  532.         pgoto(cury, curx);
  533.     }
  534.  
  535.     switch (c = getch()) {
  536.     case 'k':
  537.     case '8':
  538.     case KEY_UP:
  539.         ny = cury + BDEPTH - 1;
  540.         nx = curx;
  541.         break;
  542.     case 'j':
  543.     case '2':
  544.     case KEY_DOWN:
  545.         ny = cury + 1;
  546.         nx = curx;
  547.         break;
  548.     case 'h':
  549.     case '4':
  550.     case KEY_LEFT:
  551.         ny = cury;
  552.         nx = curx + BWIDTH - 1;
  553.         break;
  554.     case 'l':
  555.     case '6':
  556.     case KEY_RIGHT:
  557.         ny = cury;
  558.         nx = curx + 1;
  559.         break;
  560.     case 'y':
  561.     case '7':
  562.     case KEY_A1:
  563.         ny = cury + BDEPTH - 1;
  564.         nx = curx + BWIDTH - 1;
  565.         break;
  566.     case 'b':
  567.     case '1':
  568.     case KEY_C1:
  569.         ny = cury + 1;
  570.         nx = curx + BWIDTH - 1;
  571.         break;
  572.     case 'u':
  573.     case '9':
  574.     case KEY_A3:
  575.         ny = cury + BDEPTH - 1;
  576.         nx = curx + 1;
  577.         break;
  578.     case 'n':
  579.     case '3':
  580.     case KEY_C3:
  581.         ny = cury + 1;
  582.         nx = curx + 1;
  583.         break;
  584.     case FF:
  585.         nx = curx;
  586.         ny = cury;
  587.         (void) clearok(stdscr, TRUE);
  588.         (void) refresh();
  589.         break;
  590. #ifdef NCURSES_MOUSE_VERSION
  591.     case KEY_MOUSE:
  592.         {
  593.         MEVENT myevent;
  594.  
  595.         getmouse(&myevent);
  596.         if (atcpu
  597.             && myevent.y >= CY(0) && myevent.y <= CY(BDEPTH)
  598.             && myevent.x >= CX(0) && myevent.x <= CX(BDEPTH)) {
  599.             curx = CXINV(myevent.x);
  600.             cury = CYINV(myevent.y);
  601.             return (' ');
  602.         } else {
  603.             beep();
  604.             continue;
  605.         }
  606.         }
  607.         /* no fall through */
  608. #endif /* NCURSES_MOUSE_VERSION */
  609.  
  610.     default:
  611.         if (atcpu)
  612.         (void) mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, "      ");
  613.         else
  614.         (void) mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, "      ");
  615.         return (c);
  616.     }
  617.  
  618.     curx = nx % BWIDTH;
  619.     cury = ny % BDEPTH;
  620.     }
  621. }
  622.  
  623. static bool
  624. collidecheck(int b, int y, int x)
  625. /* is this location on the selected zboard adjacent to a ship? */
  626. {
  627.     bool collide;
  628.  
  629.     /* anything on the square */
  630.     if ((collide = IS_SHIP(board[b][x][y])) != FALSE)
  631.     return (collide);
  632.  
  633.     /* anything on the neighbors */
  634.     if (!closepack) {
  635.     int i;
  636.  
  637.     for (i = 0; i < 8; i++) {
  638.         int xend, yend;
  639.  
  640.         yend = y + yincr[i];
  641.         xend = x + xincr[i];
  642.         if (ONBOARD(xend, yend)
  643.         && IS_SHIP(board[b][xend][yend])) {
  644.         collide = TRUE;
  645.         break;
  646.         }
  647.     }
  648.     }
  649.     return (collide);
  650. }
  651.  
  652. static bool
  653. checkplace(int b, ship_t * ss, int vis)
  654. {
  655.     int l, xend, yend;
  656.  
  657.     /* first, check for board edges */
  658.     xend = ss->x + (ss->length - 1) * xincr[ss->dir];
  659.     yend = ss->y + (ss->length - 1) * yincr[ss->dir];
  660.     if (!ONBOARD(xend, yend)) {
  661.     if (vis)
  662.         switch (rnd(3)) {
  663.         case 0:
  664.         error("Ship is hanging from the edge of the world");
  665.         break;
  666.         case 1:
  667.         error("Try fitting it on the board");
  668.         break;
  669.         case 2:
  670.         error("Figure I won't find it if you put it there?");
  671.         break;
  672.         }
  673.     return (FALSE);
  674.     }
  675.  
  676.     for (l = 0; l < ss->length; ++l) {
  677.     if (collidecheck(b, ss->y + l * yincr[ss->dir], ss->x + l * xincr[ss->dir])) {
  678.         if (vis)
  679.         switch (rnd(3)) {
  680.         case 0:
  681.             error("There's already a ship there");
  682.             break;
  683.         case 1:
  684.             error("Collision alert!  Aaaaaagh!");
  685.             break;
  686.         case 2:
  687.             error("Er, Admiral, what about the other ship?");
  688.             break;
  689.         }
  690.         return (FALSE);
  691.     }
  692.     }
  693.     return (TRUE);
  694. }
  695.  
  696. static int
  697. awinna(void)
  698. {
  699.     int i, j;
  700.     ship_t *ss;
  701.  
  702.     for (i = 0; i < 2; ++i) {
  703.     ss = (i) ? cpuship : plyship;
  704.     for (j = 0; j < SHIPTYPES; ++j, ++ss)
  705.         if (ss->length > ss->hits)
  706.         break;
  707.     if (j == SHIPTYPES)
  708.         return (OTHER);
  709.     }
  710.     return (-1);
  711. }
  712.  
  713. static ship_t *
  714. hitship(int x, int y)
  715. /* register a hit on the targeted ship */
  716. {
  717.     ship_t *sb, *ss;
  718.     char sym;
  719.     int oldx, oldy;
  720.  
  721.     getyx(stdscr, oldy, oldx);
  722.     sb = (turn) ? plyship : cpuship;
  723.     if ((sym = board[OTHER][x][y]) == 0)
  724.     return ((ship_t *) NULL);
  725.     for (ss = sb; ss < sb + SHIPTYPES; ++ss)
  726.     if (ss->symbol == sym) {
  727.         if (++ss->hits < ss->length)    /* still afloat? */
  728.         return ((ship_t *) NULL);
  729.         else {        /* sunk! */
  730.         int i, j;
  731.  
  732.         if (!closepack)
  733.             for (j = -1; j <= 1; j++) {
  734.             int bx = ss->x + j * xincr[(ss->dir + 2) % 8];
  735.             int by = ss->y + j * yincr[(ss->dir + 2) % 8];
  736.  
  737.             for (i = -1; i <= ss->length; ++i) {
  738.                 int x1, y1;
  739.  
  740.                 x1 = bx + i * xincr[ss->dir];
  741.                 y1 = by + i * yincr[ss->dir];
  742.                 if (ONBOARD(x1, y1)) {
  743.                 hits[turn][x1][y1] = MARK_MISS;
  744.                 if (turn % 2 == PLAYER) {
  745.                     cgoto(y1, x1);
  746. #ifdef A_COLOR
  747.                     if (has_colors())
  748.                     attron(COLOR_PAIR(COLOR_GREEN));
  749. #endif /* A_COLOR */
  750.                     (void) addch(MARK_MISS);
  751. #ifdef A_COLOR
  752.                     attrset(0);
  753. #endif /* A_COLOR */
  754.                 } else {
  755.                     pgoto(y1, x1);
  756.                     (void) addch(SHOWSPLASH);
  757.                 }
  758.                 }
  759.             }
  760.             }
  761.  
  762.         for (i = 0; i < ss->length; ++i) {
  763.             int x1 = ss->x + i * xincr[ss->dir];
  764.             int y1 = ss->y + i * yincr[ss->dir];
  765.  
  766.             hits[turn][x1][y1] = ss->symbol;
  767.             if (turn % 2 == PLAYER) {
  768.             cgoto(y1, x1);
  769.             (void) addch((chtype) (ss->symbol));
  770.             } else {
  771.             pgoto(y1, x1);
  772. #ifdef A_COLOR
  773.             if (has_colors())
  774.                 attron(COLOR_PAIR(COLOR_RED));
  775. #endif /* A_COLOR */
  776.             (void) addch(SHOWHIT);
  777. #ifdef A_COLOR
  778.             attrset(0);
  779. #endif /* A_COLOR */
  780.             }
  781.         }
  782.  
  783.         (void) move(oldy, oldx);
  784.         return (ss);
  785.         }
  786.     }
  787.     (void) move(oldy, oldx);
  788.     return ((ship_t *) NULL);
  789. }
  790.  
  791. static bool
  792. plyturn(void)
  793. {
  794.     ship_t *ss;
  795.     bool hit;
  796.     NCURSES_CONST char *m = NULL;
  797.  
  798.     prompt(1, "Where do you want to shoot? ", "");
  799.     for (;;) {
  800.     (void) getcoord(COMPUTER);
  801.     if (hits[PLAYER][curx][cury]) {
  802.         prompt(1, "You shelled this spot already! Try again.", "");
  803.         beep();
  804.     } else
  805.         break;
  806.     }
  807.     hit = IS_SHIP(board[COMPUTER][curx][cury]);
  808.     hits[PLAYER][curx][cury] = (hit ? MARK_HIT : MARK_MISS);
  809.     cgoto(cury, curx);
  810. #ifdef A_COLOR
  811.     if (has_colors()) {
  812.     if (hit)
  813.         attron(COLOR_PAIR(COLOR_RED));
  814.     else
  815.         attron(COLOR_PAIR(COLOR_GREEN));
  816.     }
  817. #endif /* A_COLOR */
  818.     (void) addch((chtype) hits[PLAYER][curx][cury]);
  819. #ifdef A_COLOR
  820.     attrset(0);
  821. #endif /* A_COLOR */
  822.  
  823.     prompt(1, "You %s.", hit ? "scored a hit" : "missed");
  824.     if (hit && (ss = hitship(curx, cury))) {
  825.     switch (rnd(5)) {
  826.     case 0:
  827.         m = " You sank my %s!";
  828.         break;
  829.     case 1:
  830.         m = " I have this sinking feeling about my %s....";
  831.         break;
  832.     case 2:
  833.         m = " My %s has gone to Davy Jones's locker!";
  834.         break;
  835.     case 3:
  836.         m = " Glub, glub -- my %s is headed for the bottom!";
  837.         break;
  838.     case 4:
  839.         m = " You'll pick up survivors from my %s, I hope...!";
  840.         break;
  841.     }
  842.     (void) printw(m, ss->name);
  843.     (void) beep();
  844.     return (awinna() == -1);
  845.     }
  846.     return (hit);
  847. }
  848.  
  849. static int
  850. sgetc(const char *s)
  851. {
  852.     const char *s1;
  853.     int ch;
  854.  
  855.     (void) refresh();
  856.     for (;;) {
  857.     ch = getch();
  858.     if (islower(ch))
  859.         ch = toupper(ch);
  860.     if (ch == CTRLC)
  861.         uninitgame(0);
  862.     for (s1 = s; *s1 && ch != *s1; ++s1)
  863.         continue;
  864.     if (*s1) {
  865.         (void) addch((chtype) ch);
  866.         (void) refresh();
  867.         return (ch);
  868.     }
  869.     }
  870. }
  871.  
  872. static void
  873. randomfire(int *px, int *py)
  874. /* random-fire routine -- implements simple diagonal-striping strategy */
  875. {
  876.     static int turncount = 0;
  877.     static int srchstep = BEGINSTEP;
  878.     static int huntoffs;    /* Offset on search strategy */
  879.     int ypossible[BWIDTH * BDEPTH], xpossible[BWIDTH * BDEPTH], nposs;
  880.     int ypreferred[BWIDTH * BDEPTH], xpreferred[BWIDTH * BDEPTH], npref;
  881.     int x, y, i;
  882.  
  883.     if (turncount++ == 0)
  884.     huntoffs = rnd(srchstep);
  885.  
  886.     /* first, list all possible moves */
  887.     nposs = npref = 0;
  888.     for (x = 0; x < BWIDTH; x++)
  889.     for (y = 0; y < BDEPTH; y++)
  890.         if (!hits[COMPUTER][x][y]) {
  891.         xpossible[nposs] = x;
  892.         ypossible[nposs] = y;
  893.         nposs++;
  894.         if (((x + huntoffs) % srchstep) != (y % srchstep)) {
  895.             xpreferred[npref] = x;
  896.             ypreferred[npref] = y;
  897.             npref++;
  898.         }
  899.         }
  900.  
  901.     if (npref) {
  902.     i = rnd(npref);
  903.  
  904.     *px = xpreferred[i];
  905.     *py = ypreferred[i];
  906.     } else if (nposs) {
  907.     i = rnd(nposs);
  908.  
  909.     *px = xpossible[i];
  910.     *py = ypossible[i];
  911.  
  912.     if (srchstep > 1)
  913.         --srchstep;
  914.     } else {
  915.     error("No moves possible?? Help!");
  916.     ExitProgram(EXIT_FAILURE);
  917.     /*NOTREACHED */
  918.     }
  919. }
  920.  
  921. #define S_MISS    0
  922. #define S_HIT    1
  923. #define S_SUNK    -1
  924.  
  925. static int
  926. cpufire(int x, int y)
  927. /* fire away at given location */
  928. {
  929.     bool hit, sunk;
  930.     ship_t *ss = NULL;
  931.  
  932.     hits[COMPUTER][x][y] = (hit = (board[PLAYER][x][y])) ? MARK_HIT : MARK_MISS;
  933.     (void) mvprintw(PROMPTLINE, 0,
  934.             "I shoot at %c%d. I %s!", y + 'A', x, hit ? "hit" :
  935.             "miss");
  936.     if ((sunk = (hit && (ss = hitship(x, y)))) != 0)
  937.     (void) printw(" I've sunk your %s", ss->name);
  938.     (void) clrtoeol();
  939.  
  940.     pgoto(y, x);
  941. #ifdef A_COLOR
  942.     if (has_colors()) {
  943.     if (hit)
  944.         attron(COLOR_PAIR(COLOR_RED));
  945.     else
  946.         attron(COLOR_PAIR(COLOR_GREEN));
  947.     }
  948. #endif /* A_COLOR */
  949.     (void) addch((chtype) (hit ? SHOWHIT : SHOWSPLASH));
  950. #ifdef A_COLOR
  951.     attrset(0);
  952. #endif /* A_COLOR */
  953.  
  954.     return ((hit ? (sunk ? S_SUNK : S_HIT) : S_MISS) ? TRUE : FALSE);
  955. }
  956.  
  957. /*
  958.  * This code implements a fairly irregular FSM, so please forgive the rampant
  959.  * unstructuredness below. The five labels are states which need to be held
  960.  * between computer turns.
  961.  */
  962. static bool
  963. cputurn(void)
  964. {
  965. #define POSSIBLE(x, y)    (ONBOARD(x, y) && !hits[COMPUTER][x][y])
  966. #define RANDOM_FIRE    0
  967. #define RANDOM_HIT    1
  968. #define HUNT_DIRECT    2
  969. #define FIRST_PASS    3
  970. #define REVERSE_JUMP    4
  971. #define SECOND_PASS    5
  972.     static int next = RANDOM_FIRE;
  973.     static bool used[4];
  974.     static ship_t ts;
  975.     int navail, x, y, d, n;
  976.     int hit = S_MISS;
  977.  
  978.     switch (next) {
  979.     case RANDOM_FIRE:        /* last shot was random and missed */
  980.       refire:
  981.     randomfire(&x, &y);
  982.     if (!(hit = cpufire(x, y)))
  983.         next = RANDOM_FIRE;
  984.     else {
  985.         ts.x = x;
  986.         ts.y = y;
  987.         ts.hits = 1;
  988.         next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT;
  989.     }
  990.     break;
  991.  
  992.     case RANDOM_HIT:        /* last shot was random and hit */
  993.     used[E / 2] = used[S / 2] = used[W / 2] = used[N / 2] = FALSE;
  994.     /* FALLTHROUGH */
  995.  
  996.     case HUNT_DIRECT:        /* last shot hit, we're looking for ship's long axis */
  997.     for (d = navail = 0; d < 4; d++) {
  998.         x = ts.x + xincr[d * 2];
  999.         y = ts.y + yincr[d * 2];
  1000.         if (!used[d] && POSSIBLE(x, y))
  1001.         navail++;
  1002.         else
  1003.         used[d] = TRUE;
  1004.     }
  1005.     if (navail == 0)    /* no valid places for shots adjacent... */
  1006.         goto refire;    /* ...so we must random-fire */
  1007.     else {
  1008.         for (d = 0, n = rnd(navail) + 1; n; n--)
  1009.         while (used[d])
  1010.             d++;
  1011.  
  1012.         assert(d <= 4);
  1013.  
  1014.         used[d] = FALSE;
  1015.         x = ts.x + xincr[d * 2];
  1016.         y = ts.y + yincr[d * 2];
  1017.  
  1018.         assert(POSSIBLE(x, y));
  1019.  
  1020.         if (!(hit = cpufire(x, y)))
  1021.         next = HUNT_DIRECT;
  1022.         else {
  1023.         ts.x = x;
  1024.         ts.y = y;
  1025.         ts.dir = d * 2;
  1026.         ts.hits++;
  1027.         next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
  1028.         }
  1029.     }
  1030.     break;
  1031.  
  1032.     case FIRST_PASS:        /* we have a start and a direction now */
  1033.     x = ts.x + xincr[ts.dir];
  1034.     y = ts.y + yincr[ts.dir];
  1035.     if (POSSIBLE(x, y) && (hit = cpufire(x, y))) {
  1036.         ts.x = x;
  1037.         ts.y = y;
  1038.         ts.hits++;
  1039.         next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
  1040.     } else
  1041.         next = REVERSE_JUMP;
  1042.     break;
  1043.  
  1044.     case REVERSE_JUMP:        /* nail down the ship's other end */
  1045.     d = ts.dir + 4;
  1046.     x = ts.x + ts.hits * xincr[d];
  1047.     y = ts.y + ts.hits * yincr[d];
  1048.     if (POSSIBLE(x, y) && (hit = cpufire(x, y))) {
  1049.         ts.x = x;
  1050.         ts.y = y;
  1051.         ts.dir = d;
  1052.         ts.hits++;
  1053.         next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS;
  1054.     } else
  1055.         next = RANDOM_FIRE;
  1056.     break;
  1057.  
  1058.     case SECOND_PASS:        /* kill squares not caught on first pass */
  1059.     x = ts.x + xincr[ts.dir];
  1060.     y = ts.y + yincr[ts.dir];
  1061.     if (POSSIBLE(x, y) && (hit = cpufire(x, y))) {
  1062.         ts.x = x;
  1063.         ts.y = y;
  1064.         ts.hits++;
  1065.         next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS;
  1066.         break;
  1067.     } else
  1068.         next = RANDOM_FIRE;
  1069.     break;
  1070.     }
  1071.  
  1072.     /* check for continuation and/or winner */
  1073.     if (salvo) {
  1074.     (void) refresh();
  1075.     (void) sleep(1);
  1076.     }
  1077.     if (awinna() != -1)
  1078.     return (FALSE);
  1079.  
  1080. #ifdef DEBUG
  1081.     (void) mvprintw(PROMPTLINE + 2, 0,
  1082.             "New state %d, x=%d, y=%d, d=%d",
  1083.             next, x, y, d);
  1084. #endif /* DEBUG */
  1085.     return ((hit) ? TRUE : FALSE);
  1086. }
  1087.  
  1088. static int
  1089. playagain(void)
  1090. {
  1091.     int j;
  1092.     ship_t *ss;
  1093.  
  1094.     for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
  1095.     for (j = 0; j < ss->length; j++) {
  1096.         cgoto(ss->y + j * yincr[ss->dir], ss->x + j * xincr[ss->dir]);
  1097.         (void) addch((chtype) ss->symbol);
  1098.     }
  1099.  
  1100.     if (awinna())
  1101.     ++cpuwon;
  1102.     else
  1103.     ++plywon;
  1104.     j = 18 + strlen(name);
  1105.     if (plywon >= 10)
  1106.     ++j;
  1107.     if (cpuwon >= 10)
  1108.     ++j;
  1109.     (void) mvprintw(1, (COLWIDTH - j) / 2,
  1110.             "%s: %d     Computer: %d", name, plywon, cpuwon);
  1111.  
  1112.     prompt(2, (awinna())? "Want to be humiliated again, %s [yn]? "
  1113.        : "Going to give me a chance for revenge, %s [yn]? ", name);
  1114.     return (sgetc("YN") == 'Y');
  1115. }
  1116.  
  1117. static void
  1118. do_options(int c, char *op[])
  1119. {
  1120.     register int i;
  1121.  
  1122.     if (c > 1) {
  1123.     for (i = 1; i < c; i++) {
  1124.         switch (op[i][0]) {
  1125.         default:
  1126.         case '?':
  1127.         (void) fprintf(stderr, "Usage: battle [-s | -b] [-c]\n");
  1128.         (void) fprintf(stderr, "\tWhere the options are:\n");
  1129.         (void) fprintf(stderr, "\t-s : play a salvo game\n");
  1130.         (void) fprintf(stderr, "\t-b : play a blitz game\n");
  1131.         (void) fprintf(stderr, "\t-c : ships may be adjacent\n");
  1132.         ExitProgram(EXIT_FAILURE);
  1133.         break;
  1134.         case '-':
  1135.         switch (op[i][1]) {
  1136.         case 'b':
  1137.             blitz = 1;
  1138.             if (salvo == 1) {
  1139.             (void) fprintf(stderr,
  1140.                        "Bad Arg: -b and -s are mutually exclusive\n");
  1141.             ExitProgram(EXIT_FAILURE);
  1142.             }
  1143.             break;
  1144.         case 's':
  1145.             salvo = 1;
  1146.             if (blitz == 1) {
  1147.             (void) fprintf(stderr,
  1148.                        "Bad Arg: -s and -b are mutually exclusive\n");
  1149.             ExitProgram(EXIT_FAILURE);
  1150.             }
  1151.             break;
  1152.         case 'c':
  1153.             closepack = 1;
  1154.             break;
  1155.         default:
  1156.             (void) fprintf(stderr,
  1157.                    "Bad arg: type \"%s ?\" for usage message\n",
  1158.                    op[0]);
  1159.             ExitProgram(EXIT_FAILURE);
  1160.         }
  1161.         }
  1162.     }
  1163.     }
  1164. }
  1165.  
  1166. static int
  1167. scount(int who)
  1168. {
  1169.     register int i, shots;
  1170.     register ship_t *sp;
  1171.  
  1172.     if (who)
  1173.     sp = cpuship;        /* count cpu shots */
  1174.     else
  1175.     sp = plyship;        /* count player shots */
  1176.  
  1177.     for (i = 0, shots = 0; i < SHIPTYPES; i++, sp++) {
  1178.     if (sp->hits >= sp->length)
  1179.         continue;        /* dead ship */
  1180.     else
  1181.         shots++;
  1182.     }
  1183.     return (shots);
  1184. }
  1185.  
  1186. int
  1187. main(int argc, char *argv[])
  1188. {
  1189.     setlocale(LC_ALL, "");
  1190.  
  1191.     do_options(argc, argv);
  1192.  
  1193.     intro();
  1194.     do {
  1195.     initgame();
  1196.     while (awinna() == -1) {
  1197.         if (!blitz) {
  1198.         if (!salvo) {
  1199.             if (turn)
  1200.             (void) cputurn();
  1201.             else
  1202.             (void) plyturn();
  1203.         } else {
  1204.             register int i;
  1205.  
  1206.             i = scount(turn);
  1207.             while (i--) {
  1208.             if (turn) {
  1209.                 if (cputurn() && awinna() != -1)
  1210.                 i = 0;
  1211.             } else {
  1212.                 if (plyturn() && awinna() != -1)
  1213.                 i = 0;
  1214.             }
  1215.             }
  1216.         }
  1217.         } else
  1218.         while (turn ? cputurn() : plyturn())
  1219.             continue;
  1220.         turn = OTHER;
  1221.     }
  1222.     } while
  1223.     (playagain());
  1224.     uninitgame(0);
  1225.     /*NOTREACHED */
  1226. }
  1227.  
  1228. /* bs.c ends here */
  1229.